package org.rency.common.memcache.support.annotation.advice;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.rency.common.memcache.support.annotation.MCacheDomain;
import org.rency.common.memcache.support.annotation.MCacheInvalid;
import org.rency.common.memcache.support.annotation.MCacheKeyGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class MCacheAdviceBase {

	protected static final Logger logger = LoggerFactory.getLogger(MCacheAdviceBase.class);

	protected MCacheDomain buildCacheKey(ProceedingJoinPoint pjp, Method currentMethod, Annotation annotation) throws Throwable {
		String cacheKey = "";
		String namespace = String.valueOf(quietGetFromAnnotation("namespace", annotation));
		Annotation[][] paramAnnotations = currentMethod.getParameterAnnotations();
		if (paramAnnotations != null && paramAnnotations.length != 0) {
			Object[] args = pjp.getArgs();
			for (int i = 0; i < paramAnnotations.length; i++) {
				Annotation[] annotations = paramAnnotations[i];
				Object arg = args[i];
				for (int j = 0; j < annotations.length; j++) {
					Annotation paramAnnotation = annotations[j];
					if (paramAnnotation instanceof MCacheKeyGenerator) {// 该注解为缓存key生成器
						String[] keyMethod = (String[]) quietGetFromAnnotation("keyMethod", paramAnnotation);
						// key method must return a string value as the key
						if (arg instanceof Collection<?>) {// 如果是集合，则调用集合元素的keyMethod方法
							for (Object item : (Collection<?>) arg) {
								if (item != null) {
									cacheKey += buildKey(item, keyMethod);
								} else if ((Boolean) quietGetFromAnnotation("couldBeNull", paramAnnotation)) {
									cacheKey += "null";
								}
							}
						} else {
							// 参数为空的情况需要判断
							if (arg == null) {
								if ((Boolean) quietGetFromAnnotation("couldBeNull", paramAnnotation)) {
									cacheKey += "null";
								} else {
									throw new RuntimeException("Cache Data Key Is Empty.");
								}
							} else {
								cacheKey += buildKey(arg, keyMethod);
							}
						}
					}
				}
			}
		}else{
			cacheKey += currentMethod.getName();
		}
		MCacheDomain domain = new MCacheDomain();
		domain.setCacheKey(namespace+"."+cacheKey);
		if(!(annotation instanceof MCacheInvalid)){
			Integer expireSecond = Integer.parseInt(String.valueOf(quietGetFromAnnotation("expireSecond", annotation)));
			domain.setExpireTime( (expireSecond == null|| expireSecond <= 0) ? 0 : expireSecond);
		}
		return domain;
	}

	private String buildKey(Object item, String[] keyMethod) throws IllegalArgumentException, SecurityException,
			IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		StringBuffer buf = new StringBuffer();
		for (String km : keyMethod) {
			Object result = item.getClass().getMethod(km).invoke(item);
			buf.append(result == null ? "null" : result.toString());
		}

		return buf.toString();
	}

	/**
	 * 获取注解中的参数
	 * </br>Created on 2016年1月25日
	 * @param methodName
	 * @param annotation
	 * @return
	 */
	protected Object quietGetFromAnnotation(String methodName, Annotation annotation) {
		if (annotation == null) {
			return null;
		}
		try {
			return annotation.annotationType().getDeclaredMethod(methodName).invoke(annotation);
		} catch (Exception e) {
			logger.error("parse annotation property 'keyMethod' parameters error.",e.getMessage());
		}
		return null;
	}

	protected Method getCurrentMethod(ProceedingJoinPoint pjp) throws Throwable {
        Signature sig = pjp.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("this annotation only use on method!");
        }
        msig = (MethodSignature) sig;
        Object target = pjp.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        return currentMethod;
    }
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected Annotation getMethodAnnotation(Method method, Class annotationClass) {
        return method.getAnnotation(annotationClass);
    }
	
	protected Object getCacheData(ProceedingJoinPoint pjp,Annotation annotation){
		Integer offset = Integer.parseInt(String.valueOf(quietGetFromAnnotation("offset", annotation))) - 1;
		if(offset < 0){
			offset = 0;
		}
		
		Object[] args = pjp.getArgs();
		return args[offset];
	}
	
}